﻿using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Markup;
using System.Xml;

namespace WpfUIUtilities.Behaviors
{
  /// <summary>
  /// Class used to be able to use formatted text for TextBlock elements.
  /// Usage: Add the following attribute to a TextBlock:
  /// Behaviors:TextBlockFormattingBehaviour.FormattedText="{Binding Path=Value}"
  /// where Value will be provided by the view model.
  /// Caution: Value may not contain xml data. Instead change it like this:
  /// string.Format("<![CDATA[{0}]]>", _value);
  /// </summary>
  public class TextBlockFormattingBehaviour
  {
    public static readonly DependencyProperty FormattedTextProperty = DependencyProperty.RegisterAttached(
    "FormattedText",
    typeof(string),
    typeof(TextBlockFormattingBehaviour),
    new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.AffectsMeasure, FormattedTextPropertyChanged));

    public static void SetFormattedText(DependencyObject textBlock, string value)
    {
      textBlock.SetValue(FormattedTextProperty, value);
    }

    public static string GetFormattedText(DependencyObject textBlock)
    {
      return (string)textBlock.GetValue(FormattedTextProperty);
    }

    private static void FormattedTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
      var textBlock = d as TextBlock;

      if (textBlock == null)
      {
        return;
      }

      var formattedText = (string)e.NewValue ?? string.Empty;
      formattedText = new string(formattedText.Where(c => !char.IsControl(c)).ToArray()); // Remove control characters
      formattedText = string.Format("<Span xml:space=\"preserve\" xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">{0}</Span>", formattedText);
      
      textBlock.Inlines.Clear();
      using (var xmlReader = XmlReader.Create(new StringReader(formattedText)))
      {
        var result = (Span) XamlReader.Load(xmlReader);
        textBlock.Inlines.Add(result);
      }
    }
  }
}
